// Copyright (C) 1995
// Use is governed by the MiscKit license

#import "Time.h"
#import <string.h>
#import <sys/time.h>

#define SIZE_OF_TIME_STRING 27
#define SIZE_OF_MNAME 10
#define SIZE_OF_MABRNAME 4
#define SIZE_OF_DNAME 10
#define SIZE_OF_DABRNAME 4

@implementation Time

//--------------------------------

- free
{
	int i;
	if( timeString )
		NXZoneFree([ self zone ], timeString );
	if( timesPtr )
		NXZoneFree( [self zone], timesPtr );
	for(i=0;i<12;i++){
		NXZoneFree( [self zone], mNames[i] );
		NXZoneFree( [self zone], mAbrNames[i] );
	}
	for(i=0;i<7;i++){
		NXZoneFree( [self zone], dNames[i] );
		NXZoneFree( [self zone], dAbrNames[i] );
	}
	return [super free];
}

- init
{
	[super init];
	timesPtr = NULL;
	myTime = 0;
	[self _syncTimesStruct];
	[self initNames];
	return self;
}

- initWithCurrentTime
{
	[self init];
	[self setToCurrentTime];
	return self;
}

- initNames
{
	int i;
	for(i=0;i<12;i++){
		mNames[i] = NXZoneMalloc( [self zone], SIZE_OF_MNAME * 12 );
		mAbrNames[i] = NXZoneMalloc( [self zone], SIZE_OF_MABRNAME * 12 );
	}

	for(i=0;i<7;i++){
		dNames[i] = NXZoneMalloc( [self zone], SIZE_OF_DNAME * 7 );
		dAbrNames[i] = NXZoneMalloc( [self zone], SIZE_OF_DABRNAME * 7 );
	}

	mNames[0] =	"January";	mAbrNames[0] =	"Jan";
	mNames[1] =	"Febuary";	mAbrNames[1] =	"Feb";
	mNames[2] =	"March";	mAbrNames[2] =	"Mar";
	mNames[3] =	"April";	mAbrNames[3] =	"Apr";
	mNames[4] =	"May";		mAbrNames[4] =	"May";
	mNames[5] =	"June";		mAbrNames[5] =	"Jun";
	mNames[6] =	"July";		mAbrNames[6] =	"Jul";
	mNames[7] =	"August";	mAbrNames[7] =	"Aug";
	mNames[8] =	"September";	mAbrNames[8] =	"Sep";
	mNames[9] =	"October";	mAbrNames[9] =	"Oct";
	mNames[10] =	"November";	mAbrNames[10] =	"Nov";
	mNames[11] =	"December";	mAbrNames[11] =	"Dec";

	dNames[0] =	"Sunday";	dAbrNames[0] = 	"Sun";
	dNames[1] =	"Monday";	dAbrNames[1] = 	"Mon";
	dNames[2] =	"Tuesday";	dAbrNames[2] = 	"Tue";
	dNames[3] =	"Wednesday";	dAbrNames[3] = 	"Wed";
	dNames[4] =	"Thursday";	dAbrNames[4] = 	"Thu";
	dNames[5] =	"Friday";	dAbrNames[5] = 	"Fri";
	dNames[6] =	"Saturday";	dAbrNames[6] = 	"Sat";

	return self;
}

//--------------------------------

- (BOOL) abrMode
{
	return theAbrMode;
}

- (BOOL) setAbrMode: (BOOL)mode
{
	return (theAbrMode = mode);
}
- (long) indexOfDayName: (const char *)dayName
{
	[self notImplemented:_cmd];
	return 0;
}

- (const char *) nameOfDay: (long)dayIndex
{
	if((dayIndex>=1)&&(dayIndex<=7)){
		if([self abrMode])
			return dAbrNames[dayIndex-1];
		return dNames[dayIndex-1];
	}
	return NULL;
}

- (const char *) nameOfDay
{
	return [self nameOfDay:[self dayOfWeek]];
}

- (long) indexOfMonthName: (const char *)monthName
{
	[self notImplemented:_cmd];
	return 0;
}

- (const char *) nameOfMonth: (long)monthIndex
{
	if((monthIndex>=1)&&(monthIndex<=12)){
		if([self abrMode])
			return mAbrNames[monthIndex-1];
		return mNames[monthIndex-1];
	}
	return mNames[monthIndex];
}

- (const char *) nameOfMonth
{
	return [self nameOfMonth:[self month]];
}

- (const char *) timeString
{
	char *tmpPtr;
	if( !timeString )
		timeString = NXZoneMalloc( [self zone], SIZE_OF_TIME_STRING );
	tmpPtr = ctime( &myTime );
	bcopy( tmpPtr, timeString, strlen( tmpPtr ));
	return timeString;
}

- (const char *) timeZone
{
	if( timesPtr )
		return timesPtr->tm_zone;
	return NULL;
}

//--------------------------------

- (const time_t *)getTime_t
{
	return &myTime;
}

- (BOOL) leapYear: (long)year
{
	return (((year % 4) == 0) && ((year % 400) != 0))?YES:NO;
}

- (BOOL) leapYear
{
	return([self leapYear:[self year]]);
}

- (long) daysInYear: (long)year
{
	if([self leapYear])
		return 366;
	return 355;
}

- (long) daysInYear
{
	return [self daysInYear:[self year]];
}

- (long) daysInMonth: (long)monthIndex forYear: (long)year
{
/*30*/	if((monthIndex==4)||
	   (monthIndex==6)||
	   (monthIndex==9)||
	   (monthIndex==11))	return 30; 	
/*31*/	if((monthIndex==1)||
	   (monthIndex==3)||
	   (monthIndex==5)||
	   (monthIndex==7)||
	   (monthIndex==8)||
	   (monthIndex==12))	return 31;
/*28*/	if((monthIndex==2)&&![self leapYear:year])
				return 28;
/*29*/	else			return 29;
	return 0;
}

- (long) daysInMonth: (long)monthIndex
{
	return [self daysInMonth:monthIndex forYear:[self year]];
}

- (long) daysInMonth
{
	return [self daysInMonth:[self month] forYear:[self year]];
}

- (long) dayOfMonth
{
	if( timesPtr )
		return timesPtr->tm_mday + 1;
	else
		return 0;
}

- (long) dayOfWeek
{
	if( timesPtr )
		return timesPtr->tm_wday + 1;
	else
		return 0;
}

- (long) weekOfMonth
{
	if( timesPtr )
		return (timesPtr->tm_mday / 7) + 1;
	return 0;
}

- (long) dayOfYear
{
	return [self day];
}

- (long) year
{
	if( timesPtr )
		return timesPtr->tm_year + 1900;
	return 0;
}

- (long) month
{
	if( timesPtr )
		return timesPtr->tm_mon + 1;
	return 0;
}

- (long) week
{
	if( timesPtr )
		return (timesPtr->tm_yday / 7) + 1;
	return 0;
}

- (long) day
{
	if( timesPtr )
		return timesPtr->tm_yday + 1;
	return 0;
}

- (long) hour
{
	if( timesPtr )
		return timesPtr->tm_hour;
	return 0;
}

- (long) minute
{
	if( timesPtr )
		return timesPtr->tm_min;
	return 0;
}

- (long) second
{
	if( timesPtr )
		return timesPtr->tm_sec;
	return 0;
}

- (long) microSecond
{
	[self notImplemented:_cmd];
	return 0;
}

//--------------------------------

- _syncTimesStruct
{
	struct tm *tmpTimesPtr;
	
	tmpTimesPtr = localtime( &myTime);

	if( !timesPtr )
		timesPtr = NXZoneMalloc( [self zone],
			sizeof( struct tm ) );
	
	if( timesPtr )
		*timesPtr = *tmpTimesPtr;

	return self;
}

- resetTimeFromTM
{
	myTime = mktime( timesPtr );
	return self;
}

- setTime_t:(time_t)num
{
	myTime = num;
	[self _syncTimesStruct];
	return self;
}

- setToCurrentTime
{
	struct timezone tzp;
	struct timeval curTime;

	if( gettimeofday( &curTime, &tzp) == -1 )
	{
		/* call failed */
		return self;
	}
	[self setTime_t:curTime.tv_sec];
	return self;
}

- setTime:(Time *)aTimeObj
{
	if( timesPtr )
	{
		myTime = *[aTimeObj getTime_t];
		[self resetTimeFromTM];
	}
	return self;
}

- setYears:(long)num
{
	if( timesPtr )
	{
		timesPtr->tm_year = num - 1900;
		[self resetTimeFromTM];
	}
	return self;
}

- setMonths:(long)num
{
	if( timesPtr )
	{
		timesPtr->tm_mon = num - 1;
		[self resetTimeFromTM];
	}
	return self;
}

- setWeeks:(long)num
{
	[self setDays:(num-1)*7+[self dayOfWeek]];
	return self;
}

/*
????	tm_yday and tm_wday are ignored with mktime()
	reset mon to 0 then set mday to num
--->	Use the _sync.. method and just use seconds
	(above method don't work?)
*/
- setDays:(long)num
{
	if( timesPtr )
	{
		myTime = myTime + (num - [self day])*86400;
		[self _syncTimesStruct];
	}
	return self;
}

- setHours:(long)num
{
	if( timesPtr )
	{
		timesPtr->tm_hour = num;
		[self resetTimeFromTM];
	}
	return self;
}

- setMinutes:(long)num
{
	if( timesPtr )
	{
		timesPtr->tm_min = num;
		[self resetTimeFromTM];
	}
	return self;
}

- setSeconds: (long)numSeconds microSeconds: (long)numMicroSeconds
{
	if( timesPtr )
	{
		timesPtr->tm_sec = numSeconds;
		[self resetTimeFromTM];
	}
	return self;
}

- setSeconds: (long)numSeconds
{
	[self setSeconds:numSeconds microSeconds:0];
	return self;
}

- setMicroSeconds:(long)num
{
	[self notImplemented:_cmd];
	return self;
}

//--------------------------------

- addTime: (Time *)aTimeObj
{
	[self setTime_t:*[self getTime_t] + *[aTimeObj getTime_t]];
	return self;
}

- addTime_t: (time_t)num
{
	[self setTime_t:*[self getTime_t] + num];
	return self;
}

- addYears: (unsigned)num
{
	[self setYears:[self year] + num];
	return self;
}

- addMonths: (unsigned)num
{
	[self setMonths:[self month] + num];
	return self;
}

- addWeeks: (unsigned)num
{
	[self addDays:num*7];
	return self;
}

- addDays: (unsigned)num
{
	[self setDays:[self day] + num];
	return self;
}

- addHours: (unsigned)num
{
	[self setHours:[self hour] + num];
	return self;
}

- addMinutes: (unsigned)num
{
	[self setMinutes:[self minute] + num];
	return self;
}

- addSeconds: (unsigned)num
{
	[self setSeconds:[self second] + num];
	return self;
}

- addMicroSeconds: (unsigned)num
{
	[self setMicroSeconds:[self microSecond] + num];
	return self;
}

//--------------------------------

- subtractTime: (Time *)aTimeObj
{
	[self setTime_t:*[self getTime_t] - *[aTimeObj getTime_t]];
	return self;
}

- subtractTime_t: (time_t)num
{
	[self setTime_t:*[self getTime_t] - num];
	return self;
}

- subtractYears: (unsigned)num
{
	[self setYears:[self year] - num];
	return self;
}

- subtractMonths: (unsigned)num
{
	[self setMonths:[self month] - num];
	return self;
}

- subtractWeeks: (unsigned)num
{
	[self subtractDays:num*7];
	return self;
}

- subtractDays: (unsigned)num
{
	[self setDays:[self day] - num];
	return self;
}

- subtractHours: (unsigned)num
{
	[self setHours:[self hour] - num];
	return self;
}

- subtractMinutes: (unsigned)num
{
	[self setMinutes:[self minute] - num];
	return self;
}

- subtractSeconds: (unsigned)num
{
	[self setSeconds:[self second] - num];
	return self;
}

- subtractMicroSeconds: (unsigned)num
{
	[self setMicroSeconds:[self microSecond] - num];
	return self;
}

@end